import { _decorator, Vec2, color} from 'cc';
import { BoardData } from './boardData';
import { ColorMem } from './colorMem';
import { ColorPoint } from './colorPoint';
import { TEST_PLACED_RESULT } from './testPlacedResult';
const { ccclass,property } = _decorator;

@ccclass('FogGoGame')
export class FogGoGame {
    //棋盘，棋盘上有6中可能，0迷雾没落子，1显示没落子，2迷雾笼罩黑棋，4显示的黑棋，8迷雾笼罩白棋，16显示的白棋
    board: BoardData;
    //当前落子的一方
    piece: number;//ColorMem
    history: Vec2[] = new Array<Vec2>();
    blackSquare: number = 0;
    whiteSquare: number = 0;
    lastPoint: Vec2 = null;
    previousTake: Vec2 = null;
    t_p: number[];

    constructor(n: number, piece = ColorMem.C_BLACK) {
        this.board = new BoardData(n);
        this.piece = piece;
        this.t_p = new Array<number>(n * n);
    }

    floatPlaced(v: Vec2): number {
        if (this.board.isBounds(v)) {
            throw new Error("非法输入，超出棋盘范围！");
        }
        if (this.isSelfPiece(v)) { // 有己方棋子
            return TEST_PLACED_RESULT.J_JF;
        }
        if ((this.board.getBoradNum(v) & ColorMem.getOtherColor(this.piece) & ColorMem.C_SHOW) > 0) {// 有对方显示棋子
            return TEST_PLACED_RESULT.D_DF;
        }
        return TEST_PLACED_RESULT.N_NX;
    }

    canPlaced(v: Vec2): number {
        if (this.board.isBounds(v)) {
            throw new Error("非法输入，超出棋盘范围！");
        }
        if (this.board.hasStones(v)) {
            if (this.isSelfPiece(v)) { // 有己方棋子
                return TEST_PLACED_RESULT.J_JF;
            }
            return TEST_PLACED_RESULT.D_DF; // 有对方棋子
        }
        if (this.board.hasLiberty(v)) {
            return TEST_PLACED_RESULT.N_NX;
        }
        if (this.getSelfAroundPiece(v).length > 0) {
            if (this.hasLargeHasLiberty(v)) {
                return TEST_PLACED_RESULT.N_NX;
            }
        }
        if (this.canCaptureOpps(v)) {
            let t: Vec2 = this.isKo(v);
            if (t != null && t.equals(this.lastPoint) && v.equals(this.previousTake)) {
                return TEST_PLACED_RESULT.D_DJ;
            }
            return TEST_PLACED_RESULT.N_NX;
        }
        return TEST_PLACED_RESULT.J_JR;
    } 

    placed(now: Vec2): FogGoGame {
        let goResult: number = this.canPlaced(now);
        if (goResult != TEST_PLACED_RESULT.N_NX) {
            if (goResult == TEST_PLACED_RESULT.D_DF) {
                this.board.setBoradNum(now, ColorMem.C_SHOW & ColorMem.getOtherColor(this.piece));
            }
            if (goResult == TEST_PLACED_RESULT.J_JR || goResult == TEST_PLACED_RESULT.D_DJ) {
                let points: ColorPoint[] = this.getAroundColor(now);
                for (const i of points) {
                    if (i.color != this.piece) {
                        this.board.setBoradNum(i.point, ColorMem.C_SHOW & i.color);
                    }
                }
            }
            return this;
        }
        let ls: Vec2[] = this.listOppsPieces(now);
        if (ls.length > 0) {
            let sl: number = this.tryCaptureOpps(now);
            if (this.piece == ColorMem.C_BLACK) {
                this.blackSquare += sl;
            } else {
                this.whiteSquare += sl;
            }
        }
        let points: ColorPoint[] = this.getAroundColor(now);
        for (const i of points) {
            if (i.color != this.piece) {
                this.board.setBoradNum(i.point, ColorMem.C_SHOW & i.color);
            }
        }
        if (ls.length == 0) {
            this.board.setBoradNum(now, ColorMem.C_HIDE & this.piece);
        } else {
            this.board.setBoradNum(now, ColorMem.C_SHOW & this.piece);
        }
        this.lastPoint = now;
        this.piece = ColorMem.getOtherColor(this.piece);
        return this;
    }

    //is_是敌人
    isOppsPiece(v: Vec2): boolean {
        return (this.board.getBoradNum(v) & ColorMem.getOtherColor(this.piece)) > 0;
    }
    //is_是队友
    isSelfPiece(v: Vec2): boolean {
        return (this.board.getBoradNum(v) & this.piece) > 0;
    }

    //获取周围己方棋子
    getSelfAroundPiece(v: Vec2): Vec2[] {
        let varr = new Array<Vec2>();
        for (const yd of BoardData.yds) {
            let v1 = yd.add(v);
            if (!this.board.isBounds(v1) && this.isSelfPiece(v1)) {
                varr.push(v1);
            }
        }
        return varr;
    }

    //判断周围己方棋子有气吗
    private hasLargeHasLiberty(v: Vec2): boolean {
        FogGoGame.arrset(this.t_p, 0);
        BoardData.setArrNum(this.t_p, this.board.n, v, 1);
        let queue = this.getSelfAroundPiece(v);
        while (queue.length != 0) {
            let v1 = queue.shift();
            for (const yd of BoardData.yds) {
                let v2 = yd.add(v1);
                if (!this.board.isBounds(v2) && BoardData.getArrNum(this.t_p, this.board.n, v2) != 1) {
                    if (this.board.isEmpty(v2)) {
                        return true;
                    }
                    if (this.isSelfPiece(v2)) {//队友
                        queue.push(v2);
                        BoardData.setArrNum(this.t_p, this.board.n, v2, 1);
                    }
                }
            }
        }
        return false;
    }

    //能杀对面
    private canCaptureOpps(v: Vec2): boolean {
        let dms: Vec2[] = this.listOppsPieces(v);
        FogGoGame.arrset(this.t_p, 0);
        for (const dm of dms) {
            if (BoardData.getArrNum(this.t_p, this.board.n, dm) == 1) {
                continue;
            }
            let qs:boolean = true;
            BoardData.setArrNum(this.t_p, this.board.n, dm, 1);
            let queue: Vec2[] = new Array<Vec2>();
            queue.push(dm);
            while (queue.length != 0) {
                let v1: Vec2 = queue.shift();
                for (const yd of BoardData.yds) {
                    let v2: Vec2 = yd.add(v1);
                    if (v2.equals(v)) {
                        continue;
                    }
                    if (!this.board.isBounds(v2) && BoardData.getArrNum(this.t_p, this.board.n, v2) != 1) {
                        if (this.board.isEmpty(v2)) {
                            qs = false;
                            continue;
                        }
                        if (this.isOppsPiece(v2)) {
                            queue.push(v2);
                            BoardData.setArrNum(this.t_p, this.board.n, v2, 1);
                        }
                    }
                }
            }
            if (qs) {
                return qs;
            }
        }
        return false;
    }

    //获取周围对方棋子
    private listOppsPieces(v: Vec2): Vec2[] {
        let ls: Vec2[] = new Array<Vec2>();
        for (const yd of BoardData.yds) {
            let v1 = yd.add(v);
            if (!this.board.isBounds(v1) && this.isOppsPiece(v1)) {
                ls.push(v1);
            }
        }
        return ls;
    }

    //是否是打劫
    private isKo(v: Vec2): Vec2 {
        let dms: Vec2[] = this.listOppsPieces(v);
        let t: Vec2 = null;
        for (const v of dms) {
            let is_t: boolean = true;
            for (const yd of BoardData.yds) {
                let v2: Vec2 = v.add(yd);
                if (!this.board.isBounds(v2) && !v2.equals(v)) {
                    if (this.isOppsPiece(v2) || (this.board.isEmpty(v2))) {
                        is_t = false;
                        break;
                    }
                }
            }
            if (is_t == true) {
                if (t == null) {
                    t = v;
                } else {
                    return null;
                }
            }
        }
        if (t != null) {
            return t;
        }
        return null;
    }

    //获取周围情况
    private getAroundColor(v: Vec2): ColorPoint[] {
        let ls: ColorPoint[] = new Array<ColorPoint>();
        for (const yd of BoardData.yds) {
            let v1:Vec2 = yd.add(v);
            if (!this.board.isBounds(v1)) {
                let p = new ColorPoint(v1, ColorMem.getColorMemByShowOrHide(this.board.getBoradNum(v1)));
                ls.push(p);
            }
        }
        return ls;
    }

    //尝试杀对面
    private tryCaptureOpps(v: Vec2): number {
        let dms: Vec2[] = this.listOppsPieces(v);
        FogGoGame.arrset(this.t_p, 0);
        let sum = 0;
        for (const dm of dms) {
            if (BoardData.getArrNum(this.t_p, this.board.n, dm) == 1) {
                continue;
            }
            let qs = true;
            BoardData.setArrNum(this.t_p, this.board.n, dm, 1);
            let queue: Vec2[] = new Array<Vec2>();
            let ls: Vec2[] = new Array<Vec2>();
            queue.push(dm);
            ls.push(dm);
            while (queue.length != 0) {
                let v1 = queue.shift();
                for (const yd of BoardData.yds) {
                    let v2 = yd.add(v1);
                    if (v2.equals(v)) {
                        continue;
                    }
                    if (!this.board.isBounds(v2) && BoardData.getArrNum(this.t_p, this.board.n, v2) != 1) {
                        if (this.board.isEmpty(v2)) {
                            qs = false;
                        }
                        if (this.isOppsPiece(v2)) {
                            queue.push(v2);
                            ls.push(v2);
                            BoardData.setArrNum(this.t_p, this.board.n, v2, 1);
                        }
                    }
                }
            }
            if (qs) {
                for (const i of ls) {
                    this.board.setBoradNum(i, ColorMem.C_EMPTY & ColorMem.C_SHOW);
                }
                if (ls.length == 1) {
                    this.previousTake = ls[0];
                }
                sum += ls.length;
            }
        }
        if (sum != 1) {
            this.previousTake = null;
        }
        return sum;
    }

    //胜利一方
    winner(): number {
        //TODO: 不知道如何实现
        return 0;
    }

    //形式判断
    formJudgment(): BoardData {
        //TODO: 不知道如何实现
        return null;
    }

    static arrset(arr: number[], value: number) {
        for (let i = 0; i < arr.length; i++) {
            arr[i] = value;
        }
    }
    
}